<?php
/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Chat Server
// Project: molten-chat
// Developed by: Matthew Oltendorf
// Version: b2.1
// Updated on: July 24th, 2009
// Copyright (c) 2008-2009 Matthew Oltendorf
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */

// PHP: Hypertext Preprocessor Document

/*
	Unfinished Items:
		1.	Database Abstraction Layer still needs to be developed.
		2.	send () does not properly repair the file log if the current log file was accidentally deleted.
		3.	push () should have some form of min/max update speed system that bases its update speed on how much CPU usage is occuring.
		4.	Doesn't properly function the way I would want with multiple rooms at one time, needs altering.
		5.	
*/

/*
	Known Issues:
		1.	Does not work on load balanced server clusters; must be run on a single server. This is due to the shared memory cache.
		2.	connect doesn't always successfully connect to a valid room that does exist.
		3.
		4.
		5.
*/

define ('molten_chat', true, true);

function exception_error_handler ($type, $message, $file, $line)
	{
		throw new ErrorException ($message, 0, $type, $file, $line);
	}

date_default_timezone_set ('UTC');

if (
	!defined ('molten_chat_include')
)
	{
		define ('molten_chat_include', './include/', true);
	}

// Define rooms path.
define ('molten_chat_rooms', molten_chat_include . 'rooms/', true);

// Include configuration.
require (molten_chat_include . 'molten_chat_configuration.php');

// Include functions.
require (molten_chat_include . 'molten_chat_functions.php');

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: molten_chat_server
// Type: Singleton.
// Title: MoltX Chat Server Object.
// Description: Designed to be included and used to prepare XML data to be sent to the MoltX Chat Ajax Object.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
class molten_chat_server
	{
		public static $configuration = array ();

		private static $comments = array ();
		private static $directory = molten_chat_rooms, $extension = '.room.php';
		private static $identifiers = array ();
		private static $characters = array ('1', '2', '3', '4', '5', '6', '7', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z');
		public static $room = array ('data' => array ('active' => false), 'files' => array (), 'temporary' => array ('current' => 0, 'level' => 0, 'user' => 0));
		private static $cleanup = false, $threshold = 300; // Run garbage collection every five minutes.
		private static $lock = array ('global' => array ('pointer' => false, 'switch' => false), 'memory' => array ('pointer' => false, 'switch' => false), 'room' => array ('pointer' => false, 'switch' => false), 'users' => array('pointer' => false, 'switch' => false));
		private static $connected = false;
		public static $caching_modules = array ('apc', 'shmop');

		public static $formatting = array (
			'data' => "<?php\nif (\n\t!defined ('molten_chat')\n)\n\t{\n\t\tdie ('Not authorised.');\n\t}\n\nreturn %1\$s;\n?>",
			'first_log' => "<?php\nif (\n\t!defined ('molten_chat')\n)\n\t{\n\t\tdie ('Not authorised.');\n\t}\n\n\$message_header = array ('time' => %2\$u, 'message' => '%4\$s');\n\$message_log [] = array ('line' => %1\$u, 'time' => %2\$u, 'message' => '%6\$s');\n\$message_log [] = array ('line' => %5\$u, 'time' => %2\$u, 'message' => '%4\$s');\n",
			'log' => "<?php\nif (\n\t!defined ('molten_chat')\n)\n\t{\n\t\tdie ('Not authorised.');\n\t}\n\n\$message_header = array ('time' => %2\$u, 'message' => '%4\$s');\n",
			'message' => "\$message_log [] = array ('line' => %1\$u, 'time' => %2\$u, 'user' => '%3\$s', 'message' => '%4\$s');\n",
			'message_system' => "\$message_log [] = array ('line' => %1\$u, 'time' => %2\$u, 'message' => '%4\$s');\n",
			'message_header' => "\$message_header = array ('time' => %2\$u, 'message' => '%4\$s');\n"
		);

		public static $language = array (
			'log_owned' => 'Room created on %2$s at %3$s by %4$s.',
			'log_system' => 'Room was automatically generated on %2$s at %3$s.',
			'database_error' => 'There was an error connecting to the database.',
			'not_contiguous_log' => 'There was an error retrieving a contiguous chat history.',
			'room_connected' => 'Connected.',
			'room_disconnected' => 'Disconnected.',
			'room_not_exist' => 'This room does not exist.',
			'system' => 'system',
			'new_announcement' => 'New announcement'
		);

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: __construct
		// Title: Class construction.
		// Variables: None.
		// Description: Disabled.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private function __construct ()
			{
				// No further actions required.
				return (true);
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: __destruct
		// Title: Class destruction.
		// Variables: None.
		// Description: Disabled.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private function __destruct ()
			{
				// No further actions required.
				return (true);
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: foo
		// Title: Generic.
		// Variables: $foo
		//	1.	$foo: Does nothing.
		//		Default: 'foo'
		// Description: Generic.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function foo ($foo = 'foo')
			{
				// No further actions required.
				return (true);
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: create
		// Title: Create a room.
		// Variables: $room, $use, $owner, $levels, $messages, $retrieve, $age, $update, $is_input
		//	1.	$room: The room to create.
		//	2.	$use: Whether or not to automatically select the room after it is created.
		//		Default: false
		//	3.	$owner: The owner of the room. If this option is left blank, it is considered a public room.
		//		Default: false
		//	4.	$levels: The maximum number of level files to maintain; relaxed if the age of a level is less than or equal to the maximum age of levels.
		//		Default: 5
		//	5.	$messages: The maximum number of messages per level.
		//		Default: 50
		//	6.	$retrieve: The maximum number of messages to retrieve on fresh connections.
		//		Default: 100
		//	7.	$age: The maximum age (in seconds) to keep a level file; relaxed if there are as many as or less levels than the maximum levels in existance.
		//		Default: 300
		//	8.	$update: The time in seconds that the userlist is updated.
		//		Default: 30
		//	9.	$is_input: Whether or not this is unfiltered input.
		//		Default: true
		// Description: Create a new room, if the room does not exist, it returns false and does nothing (not even automatically select the room).
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		public static function create ($room_name, $use = false, $owner = false, $levels = 5, $messages = 50, $retrieve = 100, $age = 300, $update = 30, $is_input = true)
			{
				if (
					!in_array (self :: $configuration ['cache'], self :: $caching_modules) or
					!extension_loaded (self :: $configuration ['cache'])
				)
					{
						return false;
					}
				
				self :: $comments [] = 'create: called';

				// We rely on this error handler.
				set_error_handler ('exception_error_handler');

				try
					{
						// Make sure this variable is a boolean.
						$is_input = (boolean) $is_input;

						// Clean room name.
						$room_name = $is_input ?
							self :: clean_data ($room_name) :
							$room_name;

						// Typecast the room name to string.
						$room_name = (string) $room_name;

						// Cast $owner to a type we accept.
						$owner = is_string ($owner) ?
							$owner :
							false;

						// Clean username.
						$owner = $is_input ?
							self :: clean_data ($owner) :
							$owner;

						// Clean other input by typecasting it.
						$use = (boolean) $use;
						$levels = (integer) $levels;
						$messages = (integer) $messages;
						$retrieve = (integer) $retrieve;
						$age = (integer) $age;
						$update = (integer) $update;
					}
				catch (Exception $exception)
					{
						// Reset the error handler as to not interfere with code inheriting this object.
						restore_error_handler ();

						// Error, we can take input that is not standard, but not kinds that we can't convert.
						return false;
					}

				/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
				// Garbage Collection
				// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
				if (
					!self :: $cleanup
				)
					{
						$cleanup = self :: global_memory_get ('garbage');

						// Check if there is even a time set.
						if (
							!is_null ($cleanup)
						)
							{
								if (
									(time () - $cleanup) > self :: $threshold
								)
									{
										self :: $comments [] = 'create: ran garbage collection.';

										self :: collect_garbage ();
									}
							}
						else
							{
								self :: $comments [] = 'create: missing last garbage cleanup time.';
								self :: $comments [] = 'create: ran garbage collection.';

								self :: collect_garbage ();
							}

						self :: $cleanup = true;
					}
				/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
				// End Garbage Collection
				// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */

				// Lock memory.
				self :: lock_memory (true);

				// Get a list of active rooms.
				$rooms = self :: global_memory_get ('rooms');

				// Check if the memory cache is populated.
				if (
					is_null ($rooms)
				)
					{
						$rooms = array ();

						// Query the database.
						$sql = 'SELECT room_name
							FROM ' . molten_chat_rooms_table;

						// Check for connection.
						self :: database_connect ();

						$result = molten_chat_database :: query ($sql, false);

						// Convert query into an indexed array of objects.
						$result = molten_chat_database :: get_array ($result);

						// Convert indexed array of objects into an indexed array of names.
						foreach ($result as $room)
							{
								$rooms [] = $room -> room_name;
							}
					}

				// Generate safe encoding of room name to be used as a filename.
				$room_base = self :: parse_file ($room_name);

				// Generate a direct link to the room file.
				$room_file = self :: $directory . $room_base . '.data' . self :: $extension;

				// Open and/or create file.
				$pointer = fopen ($room_file, 'a+');
				
				// Acquire lock without blocking.
				while (!flock ($pointer, LOCK_EX | LOCK_NB))
					{
						// Generate a random number.
						$random = mt_rand (0, 250);

						// Sleep for 0.000 to 0.250 seconds. seconds.
						usleep (1000 * $random);
					}

				// Check if this file indeed contains something other than gibberish.
				$exists = (fread ($pointer, (5)) == '<?php');

				if (
					!$exists
				)
					{
						$created = time ();

						$input = array (
							$room_name,
							date ('l, F jS, Y', $created),
							date ('H:i:s', $created),
							(($owner !== false) ?
								$owner :
								self :: $language ['system'])
						);

						// Grab header contents to append.
						$message = ($owner !== false) ?
							vsprintf (self :: $language ['log_owned'], $input) :
							vsprintf (self :: $language ['log_system'], $input);

						$data = array (
							'active' => true,
							'announcement' => false,
							'age' => $age,
							'base' => $room_base,
							'created' => $created,
							'header' => $message,
							'levels' => $levels,
							'messages' => $messages,
							'retrieve' => $retrieve,
							'name' => $room_name,
							'owner' => $owner,
							'update' => $update
						);

						$export = var_export ($data, true);

						$export = sprintf (self :: $formatting ['data'], $export);

						// Write data contents to new room descriptor.
						fwrite ($pointer, $export);

						// Generate path to room's first log file (level 1).
						$room_log = self :: $directory . $room_base . '.0' . self :: $extension;

						// Grab header contents to append.
						//$header = file_get_contents (molten_chat_include . 'header.logs.php');

						$input = array (
							0,
							$created,
							($owner !== false) ?
								self :: parse_data ($owner) :
								'false',
							self :: parse_data ($message),
							1,
							self :: parse_data (self :: $language ['room_connected'])
						);

						$header = vsprintf (self :: $formatting ['first_log'], $input);

						$log = fopen ($room_log, 'w'); // Open file for writing.

						// Acquire lock without blocking.
						while (!flock ($log, LOCK_EX | LOCK_NB))
							{
								// Generate a random number.
								$random = mt_rand (0, 250);

								// Sleep for 0.000 to 0.250 seconds. seconds.
								usleep (1000 * $random);
							}
						fwrite ($log, $header . "\n"); // Write data.
						flock ($log, LOCK_UN); // Unlock.

						fclose ($log); // Close file.

						// Generate path to room user file.
						//$room_users = self :: $directory . $room_base . '.users.lock';

						// Grab header contents to append.
						//$header = file_get_contents (molten_chat_include . 'header.users.php');
						/*
						$input = array ();

						$header = vsprintf (self :: $formatting ['users'], $input);

						$users = fopen ($room_users, 'w'); // Open file for writing.

						// Acquire lock without blocking.
						while (!flock ($users, LOCK_EX | LOCK_NB))
							{
								// Generate a random number.
								$random = mt_rand (0, 250);

								// Sleep for 0.000 to 0.250 seconds. seconds.
								usleep (1000 * $random);
							}

						fwrite ($users, $header . "\n"); // Write data.
						flock ($users, LOCK_UN); // Unlock.

						fclose ($users); // Close file.
						*/
						if (
							$use
						)
							{
								// Set room data.
								self :: $room = array (
									'data' => $data,
									'files' => array (
										'data' => $room_file,
										'room' => self :: $directory . $room_base . '.room.lock',
										'users' => self :: $directory . $room_base . '.users.lock',
										'logs' => array ($room_log)
									)
								);

								$current = !is_null ($current = self :: room_memory_get ('current')) ?
									$current :
									self :: get_current ();
								$level = &$current ['level'];
								$line = &$current ['line'];

								// Check if the room was not updated by create.
								if (
									$level == 0 and
									$line == 0
								)
									{
										$current = array (
											'level' => 0,
											'line' => 2
										);

										// Update the room with the new line count.
										self :: room_memory_set ('current', $current, false);
									}
							}
					}

				// Check if our room is already somehow in memory.
				if (
					!in_array ($room_name, $rooms)
				)
					{
						// Append room to the current list of rooms active.
						$rooms [] = $room_name;

						// Add data to memory.
						self :: global_memory_set ('rooms', $rooms);

						// Update database.
						$sql = 'INSERT INTO ' . molten_chat_rooms_table . '
							(room_active, room_age, room_levels, room_level, room_messages, room_name, room_owner, room_activity, room_creation)
							VALUES
							(\'true\', ' . $age . ', ' . $levels . ', 0, ' . $messages . ', ' . (($owner !== false) ? '\'' . $owner . '\'' : 'NULL') . ', ' . time () . ', ' . time () . ')';

						// Check for connection.
						self :: database_connect ();

						molten_chat_database :: query ($sql, false);
					}

				// Close the file.
				flock ($pointer, LOCK_UN);
				fclose ($pointer);

				// Release memory lock.
				self :: lock_memory (false);

				// Reset the error handler as to not interfere with code inheriting this object.
				restore_error_handler ();

				// Return whether or not this room exists.
				return !$exists;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: connect
		// Title: Use a room.
		// Variables: $room_name, $is_input
		//	1.	$room_name: The room to join.
		//	2.	$is_input: Whether or not this is unfiltered input.
		//		Default: true
		// Description: Select a room to use throughout the object.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		public static function connect ($room_name, $is_input = true)
			{
				if (
					!in_array (self :: $configuration ['cache'], self :: $caching_modules) or
					!extension_loaded (self :: $configuration ['cache'])
				)
					{
						return false;
					}

				self :: $comments [] = 'connect: called';
				
				// We rely on this error handler.
				set_error_handler ('exception_error_handler');

				try
					{
						// Make sure this variable is a boolean.
						$is_input = (boolean) $is_input;

						// Clean room name.
						$room_name = $is_input ?
							self :: clean_data ($room_name) :
							$room_name;

						// Typecast the room name to string.
						$room_name = (string) $room_name;
					}
				catch (Exception $exception)
					{
						// Reset the error handler as to not interfere with code inheriting this object.
						restore_error_handler ();

						// Error, we can take input that is not standard, but not kinds that we can't convert.
						return false;
					}

				/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
				// Garbage Collection
				// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
				if (
					!self :: $cleanup
				)
					{
						$cleanup = self :: global_memory_get ('garbage');

						// Check if there is even a time set.
						if (
							!is_null ($cleanup)
						)
							{
								if (
									(time () - $cleanup) > self :: $threshold
								)
									{
										self :: $comments [] = 'create: ran garbage collection.';

										self :: collect_garbage ();
									}
							}
						else
							{
								self :: $comments [] = 'connect: missing last garbage cleanup time.';
								self :: $comments [] = 'connect: ran garbage collection.';

								self :: collect_garbage ();
							}

						self :: $cleanup = true;
					}
				/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
				// End Garbage Collection
				// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */

				// Get a list of active rooms.
				$rooms = self :: global_memory_get ('rooms');

				// Check if the memory cache is populated.
				if (
					is_null ($rooms)
				)
					{
						$rooms = array ();

						// Query the database.
						$sql = 'SELECT room_name
							FROM ' . molten_chat_rooms_table;

						// Check for connection.
						self :: database_connect ();

						$result = molten_chat_database :: query ($sql, false);

						// Convert query into an indexed array of objects.
						$result = molten_chat_database :: get_array ($result);

						// Convert indexed array of objects into an indexed array of names.
						foreach ($result as $room)
							{
								$rooms [] = $room -> room_name;
							}
					}

				// Check if our room exists.
				if (
					in_array ($room_name, $rooms)
				)
					{
						// Generate safe encoding of room name to be used as a filename.
						$room_base = self :: parse_file ($room_name);

						// Generate a direct link to the room file.
						$room_file = self :: $directory . $room_base . '.data' . self :: $extension;

						// Open and/or create file.
						$pointer = fopen ($room_file, 'a+');

						// Acquire lock without blocking.
						while (!flock ($pointer, LOCK_SH | LOCK_NB))
							{
								// Generate a random number.
								$random = mt_rand (0, 250);

								// Sleep for 0.000 to 0.250 seconds. seconds.
								usleep (1000 * $random);
							}

						$exists = (fread ($pointer, (5)) == '<?php');

						// Check if this file indeed contains something other than gibberish.
						if (
							$exists
						)
							{
								$data = include ($room_file);

								if (
									$data ['active']
								)
									{
										// Set room data.
										self :: $room = array (
											'data' => $data,
											'files' => array (
												'data' => $room_file,
												'room' => self :: $directory . $room_base . '.room.lock',
												'users' => self :: $directory . $room_base . '.users.lock',
												'logs' => array ()
											)
										);

										$current = !is_null ($current = self :: room_memory_get ('current')) ?
											$current :
											self :: get_current ();
										$level = &$current ['level'];
										$line = &$current ['line'];

										// Check if the room was not updated by create.
										if (
											$level == 0 and
											$line == 0
										)
											{
												$current = array (
													'level' => 0,
													'line' => 2
												);

												// Update the room with the new line count.
												self :: room_memory_set ('current', $current, false);
											}
									}
								else
									{
										$exists = false;
									}
							}

						// Close the file.
						flock ($pointer, LOCK_UN);
						fclose ($pointer);

						// Reset the error handler as to not interfere with code inheriting this object.
						restore_error_handler ();

						// Return whether or not this room exists.
						return $exists;
					}

				// Reset the error handler as to not interfere with code inheriting this object.
				restore_error_handler ();

				return false;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: delete
		// Title: Delete room.
		// Variables: $room_name
		//	1.	$room_name: The name of the room to delete.
		// Description: Que a room for deletion.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		public static function delete ($room_name)
			{

			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: join
		// Title: Join a room.
		// Variables: $user, $guest, $color, $permissions, $is_input
		//	1.	$user: The user that has joined the room, if this is left blank an anonymous account is used, it is then returned.
		//		Default: false
		//	2.	$guest: Whether or not this user is a guest (no permissions).
		//		Default: true
		//	3.	$color: An array containing the red, green, and blue values (from 0 through 255) for the color of the user.
		//		Default: false
		//	4.	$permissions: An indexed array of what chat functions the user can use. If set to true, all permissions are enabled.
		//		Default: false
		//	5.	$is_input: Whether or not this is unfiltered input.
		//		Default: true
		// Description: Adds a user to the room.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		public static function join ($user = false, $guest = true, $color = false, $permissions = false, $is_input = true)
			{
				// We rely on this error handler.
				set_error_handler ('exception_error_handler');

				try
					{
						// Make sure this variable is a boolean.
						$is_input = (boolean) $is_input;

						// Cast $user to a type we accept.
						$user = is_string ($user) ?
							($is_input ?
								self :: clean_data ($user) :
								$user) :
							self :: to_string ($user);

						// Check if the user is null.
						$user = !is_null ($user) ?
							$user :
							false;

						// Cast $color to a type we accept.
						$color = is_array ($color) ?
							((
								isset ($color ['blue']) and
								isset ($color ['green']) and
								isset ($color ['red'])
							) ?
								((
									is_integer ($color ['blue']) and
									is_integer ($color ['green']) and
									is_integer ($color ['red'])
								) ?
									$color :
									false) :
								false) :
							(boolean) $color;

						// Cast $permissions to a type we accept.
						$permissions = is_array ($permissions) ?
							$permissions :
							(boolean) $permissions;

						// Clean other input by typecasting it.
						$guest = (boolean) $guest;
					}
				catch (Exception $exception)
					{
						// Reset the error handler as to not interfere with code inheriting this object.
						restore_error_handler ();

						// Error, we can take input that is not standard, but not kinds that we can't convert.
						return false;
					}

				if (
					self :: $room ['data'] ['active']
				)
					{

						// Lock users.
						self :: lock_users (true);

						// We have to run all the way to the end of this function so we use this simple switch.
						$success = false;

						// Get the current users in the room.
						$users = is_array ($users = self :: room_memory_get ('users')) ?
							$users :
							array ('guests' => array (), 'users' => array ('index' => array (), 'data' => array ()));

						// Check if the user was left empty.
						if (
							$user === false
						)
							{
								$index = array_splice ($users ['guests'], -1, 1);
								$user = (key ($index) + 1);
								$guest = true;
							}
						// Check if we are joining a guest to the room, and if it's not existing.
						elseif (
							!$guest or
							(
								is_integer ($user) and
								!isset ($user ['guests'] [$user])
							)
						)
							{
								if (
									$guest
								)
									{
										// Add guest to room.
										//$users ['guests'] [$user] [] = array ();

										$success = true;
									}
								else
									{
										// Check if the user is already in the room.
										if (
											array_search ($user, $users ['users'] ['index']) === false
										)
											{
												// Add user to index.
												$users ['users'] ['index'] [] = $user;

												// Add user to room.
												$users ['users'] ['data'] [] = array (
													'color' => $color,
													'name' => $user,
													'time' => time ()
												);

												$success = true;
											}
									}
							}

						if (
							$success
						)
							{
								// Set the current users in the room.
								self :: room_memory_set ('users', $users);

								// Increment user action count by one.
								$action_time = is_integer ($action = self :: room_memory_get ('time')) ?
									$action_time :
									0;

								if (
									$action_time < (time () - self :: $room ['data'] ['update'])
								)

								// Increment user action count by one.
								$action = is_integer ($action = self :: room_memory_get ('user')) ?
									$action :
									0;
								self :: room_memory_set ('user', ++$action);
								self :: room_memory_set ('user_time', time ());
							}

						// Release users lock.
						self :: lock_users (false);

						// Reset the error handler as to not interfere with code inheriting this object.
						restore_error_handler ();

						return $success;
					}

				// Reset the error handler as to not interfere with code inheriting this object.
				restore_error_handler ();

				return false;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: update
		// Title: Update user.
		// Variables: $user, $guest, $color, $permissions, $is_input
		//	1.	$user: The user that has joined the room, if this is left blank an anonymous account is used, it is then returned.
		//		Default: false
		//	2.	$guest: Whether or not this user is a guest (no permissions).
		//		Default: true
		//	3.	$color: An array containing the red, green, and blue values (from 0 through 255) for the color of the user.
		//		Default: false
		//	4.	$permissions: An indexed array of what chat functions the user can use. If set to true, all permissions are enabled.
		//		Default: false
		//	5.	$is_input: Whether or not this is unfiltered input.
		//		Default: true
		// Description: Updates a user without causing leave/join messages.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		public static function update ($user, $guest = true, $color = false, $permissions = false, $is_input = true)
			{
				// We rely on this error handler.
				set_error_handler ('exception_error_handler');

				try
					{
						// Make sure this variable is a boolean.
						$is_input = (boolean) $is_input;

						// Cast $user to a type we accept.
						$user = is_string ($user) ?
							($is_input ?
								self :: clean_data ($user) :
								$user) :
							self :: to_string ($user);

						// Check if the user is null.
						$user = !is_null ($user) ?
							$user :
							false;

						// Cast $color to a type we accept.
						$color = is_array ($color) ?
							((
								isset ($color ['blue']) and
								isset ($color ['green']) and
								isset ($color ['red'])
							) ?
								((
									is_integer ($color ['blue']) and
									is_integer ($color ['green']) and
									is_integer ($color ['red'])
								) ?
									$color :
									false) :
								false) :
							(boolean) $color;

						// Cast $permissions to a type we accept.
						$permissions = is_array ($permissions) ?
							$permissions :
							(boolean) $permissions;

						// Clean other input by typecasting it.
						$guest = (boolean) $guest;
					}
				catch (Exception $exception)
					{
						// Reset the error handler as to not interfere with code inheriting this object.
						restore_error_handler ();

						// Error, we can take input that is not standard, but not kinds that we can't convert.
						return false;
					}

				if (
					self :: $room ['data'] ['active']
				)
					{
						$user = $is_input ?
							self :: clean_data ($user) :
							$user;

						// Lock users.
						self :: lock_users (true);

						// We have to run all the way to the end of this function so we use this simple switch.
						$success = false;

						// Get the current users in the room.
						$users = is_array ($users = self :: room_memory_get ('users')) ?
							$users :
							array ('guests' => array (), 'users' => array ('index' => array (), 'data' => array ()));

						if (
							$guest
						)
							{
								if (
									!empty ($users ['guest'] [$user])
								)
									{
										//$users ['guest'] [$user] = array ();

										$success = true;
									}
							}
						else
							{
								$index = array_search ($user, $users ['users'] ['index']);

								if (
									$index !== false
								)
									{
										$difference = (time () - $users ['users'] ['data'] [$index] ['time']);

										$users ['users'] ['data'] [$index] ['time'] = time ();

										// Update the color.
										if (
											is_array ($color)
										)
											{
												$users ['users'] ['data'] [$index] ['color'] = $color;
											}

										// Remove the color.
										if (
											$color === true
										)
											{
												$users ['users'] ['data'] [$index] ['color'] = false;
											}

										$success = true;
									}
							}

						if (
							$success
						)
							{
								// Set the current users in the room.
								self :: room_memory_set ('users', $users);

								$action_time = is_integer ($action_time = self :: room_memory_get ('user_time')) ?
									$action_time :
									0;

								if (
									$action_time < (time () - self :: $room ['data'] ['update']) or
									$difference > self :: $room ['data'] ['update']
								)
									{
										// Increment user action count by one.
										$action = is_integer ($action = self :: room_memory_get ('user')) ?
											$action :
											0;
										self :: room_memory_set ('user', ++$action);
										self :: room_memory_set ('user_time', time ());
									}
							}

						// Release users lock.
						self :: lock_users (false);

						// Reset the error handler as to not interfere with code inheriting this object.
						restore_error_handler ();

						return $success;
					}

				// Reset the error handler as to not interfere with code inheriting this object.
				restore_error_handler ();

				return false;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: leave
		// Title: Leave a room.
		// Variables: $user, $guest, $is_input
		//	1.	$user: The user that has left the room, if this is left blank an anonymous account is used.
		//	2.	$guest: If the user is a guest or not.
		//		Default: true
		//	3.	$is_input: Whether or not this is unfiltered input.
		//		Default: true
		// Description: Removes a user from the room.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		public static function leave ($user, $guest = true, $is_input = true)
			{
				// We rely on this error handler.
				set_error_handler ('exception_error_handler');

				try
					{
						// Make sure this variable is a boolean.
						$is_input = (boolean) $is_input;

						// Cast $user to a type we accept.
						$user = is_string ($user) ?
							($is_input ?
								self :: clean_data ($user) :
								$user) :
							self :: to_string ($user);

						// Check if the user is null.
						$user = !is_null ($user) ?
							$user :
							false;

						// Clean other input by typecasting it.
						$guest = (boolean) $guest;
					}
				catch (Exception $exception)
					{
						// Reset the error handler as to not interfere with code inheriting this object.
						restore_error_handler ();

						// Error, we can take input that is not standard, but not kinds that we can't convert.
						return false;
					}
					
				if (
					self :: $room ['data'] ['active']
				)
					{
						// Lock users.
						self :: lock_users (true);

						$users = self :: room_memory_get ('users');

						// We have to run all the way to the end of this function so we use this simple switch.
						$success = false;

						if (
							$guest
						)
							{
								if (
									!empty ($users ['guest'] [$user])
								)
									{
										// Remove guest from list, and reorganize count.
										//array_splice ($users ['guest'], ($user + 1), 1);

										$success = true;
									}
							}
						else
							{
								$index = array_search ($user, $users ['users'] ['index']);

								if (
									$index !== false
								)
									{
										// Remove user from index.
										array_splice ($users ['users'] ['index'], $index, 1);

										// Remove user from list.
										array_splice ($users ['users'] ['data'], $index, 1);

										$success = true;
									}
							}

						if (
							$success
						)
							{
								// Set the current users in the room.
								self :: room_memory_set ('users', $users);

								// Increment user action count by one.
								$action = is_integer ($action = self :: room_memory_get ('user')) ?
									$action :
									0;
								self :: room_memory_set ('user', ++$action);
								self :: room_memory_set ('user_time', time ());
							}

						// Release users lock.
						self :: lock_users (false);

						// Reset the error handler as to not interfere with code inheriting this object.
						restore_error_handler ();

						return $success;
					}

				// Reset the error handler as to not interfere with code inheriting this object.
				restore_error_handler ();

				return false;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: send
		// Title: Send a message.
		// Variables: $message, $user, $is_input, $call_function
		//	1.	$message: The message to send to the room.
		//	2.	$user: The user the message is sent as. If left false the message is sent as a system message.
		//		Default: false
		//	3.	$is_input: Whether or not this is unfiltered input.
		//		Default: true
		//	4.	$call_function: Whether or not to call a function if the message starts with a forward slash.
		//		Default: true
		// Description: Sends a message to a chat room.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		public static function send ($message, $user = false, $is_input = true, $call_function = true)
			{
				// We rely on this error handler.
				set_error_handler ('exception_error_handler');

				try
					{
						// Make sure this variable is a boolean.
						$is_input = (boolean) $is_input;

						// Clean the message.
						$message = $is_input ?
							self :: clean_data ($message) :
							$message;

						// Typecast the message to string.
						$message = self :: to_string ($message);

						// Cast $user to a type we accept.
						$user = is_string ($user) ?
							($is_input ?
								self :: clean_data ($user) :
								$user) :
							self :: to_string ($user);

						// Check if the user is null.
						$user = !is_null ($user) ?
							$user :
							false;

						// Clean other input by typecasting it.
						$call_function = (boolean) $call_function;
					}
				catch (Exception $exception)
					{
						// Reset the error handler as to not interfere with code inheriting this object.
						restore_error_handler ();

						// Error, we can take input that is not standard, but not kinds that we can't convert.
						return false;
					}

				// We don't replace spaces in the beginning because someone may have used the space to prevent a function call from occuring.
				$message = preg_replace (array ('/\\s{2,}/', '/\\s*$/'), array (' ', ''), $message);

				if (
					$message != '' and
					self :: $room ['data'] ['active']
				)
					{
						// Lock room.
						self :: lock_room (true);

						// Check if the message starts with a slash; indicating a command.
						if (
							$call_function and
							$message [0] == '/'
						)
							{
								// Since we don't know if this function should appear in the log or not, we just rely on the result.
								if (
									self :: call_function ($user, $message)
								)
									{
										// Release room lock.
										self :: lock_room (false);

										// Reset the error handler as to not interfere with code inheriting this object.
										restore_error_handler ();

										return true;
									}
							}
						else
							{
								// Now we strip spaces from the start.
								$message = preg_replace ('/^\\s*/', '', $message);
							}

						// Get the maximum amount of messages allowed per level.
						$limit = self :: $room ['data'] ['messages'];

						// Get the current level and message number.
						//$current = self :: room_memory_get ('current');
						//$level = self :: room_memory_get ('level');
						$current = !is_null ($current = self :: room_memory_get ('current')) ?
							$current :
							self :: get_current ();
						$level = &$current ['level'];
						$line = &$current ['line'];

						$log_file = self :: $directory . self :: $room ['data'] ['base'] . '.' . (($line > 0) ? $level : ($level - 1)) . self :: $extension;

						if (
							$line > 0 and
							$line < $limit
						)
							{
								// Get the current messages of the log.
								// Lots of memory usage here if you have large level sizes.
								$message_log = self :: room_memory_get ('message_log');

								// Uh oh, we have to manually count them... This is slow!
								if (
									!is_array ($message_log)
								)
									{
										$message_log = array ();

										// Uh oh, we also have to load them from disk, even more slow!
										include ($log_file);
									}
							}

						if (
							$line == 0
						)
							{
								$log_file = self :: $directory . self :: $room ['data'] ['base'] . '.' . $level . self :: $extension;
								$message_log = array ();

								$input = array (
									self :: $room ['data'] ['name'],
									date ('l, F jS, Y', self :: $room ['data'] ['created']),
									date ('H:i:s', self :: $room ['data'] ['created']),
									((self :: $room ['data'] ['owner'] !== false) ?
										self :: $room ['data'] ['owner'] :
										self :: $language ['system'])
								);

								// Grab header contents to append.
								$header = (self :: $room ['data'] ['owner'] !== false) ?
									vsprintf (self :: $language ['log_owned'], $input) :
									vsprintf (self :: $language ['log_system'], $input);

								$input = array (
									0,
									self :: $room ['data'] ['created'],
									((self :: $room ['data'] ['owner'] !== false) ?
										self :: parse_data (self :: $room ['data'] ['owner']) :
										'false'),
									self :: parse_data ($header)
								);

								$output = vsprintf (self :: $formatting ['log'], $input) . "\n";

								// Update database.
								$sql = '';

								self :: database_connect ();

								molten_chat_database :: query ($sql);
							}
						else
							{
								$output = '';
							}

						$formatting = ($user !== false) ?
							self :: $formatting ['message'] :
							self :: $formatting ['message_system'];

						$input = array (
							$line,
							($sent = time ()),
							self :: parse_data ($user),
							self :: parse_data ($message)
						);

						// Prepare message.
						$output .= vsprintf ($formatting, $input);

						// Open up the log.
						$pointer = fopen ($log_file, 'a+');

						// Acquire lock without blocking.
						while (!flock ($pointer, LOCK_EX | LOCK_NB))
							{
								// Generate a random number.
								$random = mt_rand (0, 250);

								// Sleep for 0.000 to 0.250 seconds. seconds.
								usleep (1000 * $random);
							}

						// Write message to log.
						fwrite ($pointer, $output);

						// Close log file.
						flock ($pointer, LOCK_UN);
						fclose ($pointer);

						// Append message to log.
						$message_log [] = array (
							'line' => $line,
							'time' => $sent,
							'user' => $user,
							'message' => $message
						);

						// Write to memory.
						self :: room_memory_set ('message_log', $message_log);

						// Finally, increment the message count.
						$current = (($line + 1) < self :: $room ['data'] ['messages']) ?
							array (
								'level' => $level,
								'line' => ($line + 1)
							) :
							array (
								'level' => ($level + 1),
								'line' => 0
							);

						// We do this last to prevent other threads from trying to read the new messages before they're written.
						self :: room_memory_set ('current', $current);

						//er (self :: get_room_identifiers ());

						// Release room lock.
						self :: lock_room (false);

						// Reset the error handler as to not interfere with code inheriting this object.
						restore_error_handler ();

						return true;
					}

				// Reset the error handler as to not interfere with code inheriting this object.
				restore_error_handler ();

				return false;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: push
		// Title: Invoke AJAX Push.
		// Variables: $level, $line, $action, $limit, $speed
		//	1.	$level: The current message log level of the instance.
		//	2.	$line: The current line of the instance.
		//	3.	$action: The current action identifier for user control.
		//	4.	$limit: The maximum time (in seconds) to hold out for before returning false.
		//		Default: 30
		//	5.	$speed: The rate at which the system checks for new messages (in milliseconds). Higher is slower, but less resource-intensive.
		//		Default: 250
		// Description: Delays the result of the transaction for a certain time until there is new information available.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		public static function push ($level, $line, $action, $limit = 30, $speed = 250)
			{
				// We rely on this error handler.
				set_error_handler ('exception_error_handler');

				$time = time ();
				$start = microtime (true) - $time;
				self :: $comments [] = 'push: started at ' . ($start + $time) . '.';

				try
					{
						// Clean other input by typecasting it.
						$level = (integer) $level;
						$line = (integer) $line;
						$action = (integer) $action;
						$limit = (integer) $limit;
						$speed = (integer) $speed;
					}
				catch (Exception $exception)
					{
						$end = microtime (true) - $time;
						$delay = $end - $start;
						self :: $comments [] = 'push: stopped at ' . ($end + $time) . '.';
						self :: $comments [] = 'push: delayed execution for ' . $delay . ' second' . (($delay < 1 or $delay > 1) ? 's' : '') . '.';

						// Reset the error handler as to not interfere with code inheriting this object.
						restore_error_handler ();

						// Error, we can take input that is not standard, but not kinds that we can't convert.
						return false;
					}

				if (
					self :: $room ['data'] ['active']
				)
					{
						set_time_limit ($limit + 30);

						// Speed should not be more than one-half of the time limit.
						$speed = $speed <= ($limit * 500) ?
							$speed :
							($limit * 500);

						$limit += time ();

						// Speed is actually in microseconds. So we must multiply it by a thousand to convert milliseconds to microseconds.
						$speed *= 1000;

						self :: $room ['temporary'] ['level'] = $level;
						self :: $room ['temporary'] ['current'] = $line;
						self :: $room ['temporary'] ['user'] = $action;

						do
							{
								// Zzzzz... We do this first to prevent request spam.
								usleep ($speed);

								// Check if anything has changed.
								if (
									self :: check_logs () or
									self :: check_users () or
									(
										$level == 0 and
										$line == 0
									)
								)
									{
										// Reset the error handler as to not interfere with code inheriting this object.
										restore_error_handler ();

										$end = microtime (true) - $time;
										$delay = $end - $start;
										self :: $comments [] = 'push: stopped at ' . ($end + $time) . '.';
										self :: $comments [] = 'push: delayed execution for ' . $delay . ' second' . (($delay < 1 or $delay > 1) ? 's' : '') . '.';

										return true;
									}
							}
						// Check if we still have time to do it again!
						while (
							time () < $limit
						);
					}

				$end = microtime (true) - $time;
				$delay = $end - $start;
				self :: $comments [] = 'push: stopped at ' . ($end + $time) . '.';
				self :: $comments [] = 'push: delayed execution for ' . $delay . ' second' . (($delay < 1 or $delay > 1) ? 's' : '') . '.';

				// Reset the error handler as to not interfere with code inheriting this object.
				restore_error_handler ();

				// Aww, nothing changed. Sad face. :(
				return false;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: retrieve
		// Title: Retrieve room data.
		// Variables: $level, $line, $action, $return
		//	1.	$level: The current message log level of the instance.
		//	2.	$line: The current line of the instance.
		//	3.	$action: The current action identifier for user control.
		//	4.	$return: Whether or not to return the output instead of echo it.
		//		Default: false
		// Description: Retrieve up to date room information; messages, users, etc.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		public static function retrieve ($level, $line, $action, $return = false)
			{
				// We rely on this error handler.
				set_error_handler ('exception_error_handler');

				try
					{
						// Clean other input by typecasting it.
						$level = (integer) $level;
						$line = (integer) $line;
						$action = (integer) $action;
						$return = (boolean) $return;
					}
				catch (Exception $exception)
					{
						// Reset the error handler as to not interfere with code inheriting this object.
						restore_error_handler ();

						// Error, we can take input that is not standard, but not kinds that we can't convert.
						return false;
					}

				if (
					self :: $room ['data'] ['active']
				)
					{
						$actions = null;
						$document = null;
						$levels = 0;
						$messages = null;
						$message_log = array ();
						$input = array ();
						$output = false;
						$users = null;

						self :: $room ['temporary'] ['level'] = $level;
						self :: $room ['temporary'] ['current'] = $line;
						self :: $room ['temporary'] ['user'] = $action;

						if (
							self :: check_logs () or
							(
								$level == 0 and
								$line == 0
							)
						)
							{
								// Get the current level and message number.
								//$level = self :: room_memory_get ('level');
								//$line = self :: room_memory_get ('current');
								$current = !is_null ($current = self :: room_memory_get ('current')) ?
									$current :
									self :: get_current ();
								$level = $current ['level'];
								$line = $current ['line'];

								// Check if we need to load any previous logs.
								if (
									$level > 0 and
									(
										self :: $room ['temporary'] ['level'] < ($level - 1) or
										(
											self :: $room ['temporary'] ['level'] == 0 and
											self :: $room ['temporary'] ['current'] == 0
										) or
										(
											self :: $room ['temporary'] ['level'] < $level and
											$line > 0
										)
									)
								)
									{
										if (
											self :: $room ['temporary'] ['level'] < $level and
											(
												self :: $room ['temporary'] ['level'] != 0 or
												self :: $room ['temporary'] ['current'] != 0
											)
										)
											{
												// Get the number of messages that have been sent since the last connection.
												//$retrieve = ($level * $line) - (self :: $room ['temporary'] ['level'] * self :: $room ['temporary'] ['current']);

												// Get the amount of levels needed to be loaded to retrieve all the messages necesarry to populate the log.
												//$levels = ceil ($retrieve / self :: $room ['data'] ['messages']);
												$levels = $level - self :: $room ['temporary'] ['level'];
											}
										else
											{
												// Get the amount of levels needed to be loaded to retrieve all the messages necesarry to populate the log.
												$levels = ceil (self :: $room ['data'] ['retrieve'] / self :: $room ['data'] ['messages']);
												$excess = ($level - $levels);
												$levels = ($excess >= 0) ?
													$levels :
													($levels + $excess);
												$levels = $levels > self :: $room ['data'] ['levels'] ?
													self :: $room ['data'] ['levels'] :
													$levels;
											}

										// Whoo backwards.
										for ($i = $levels, $minimum = (($line > 0) ? 0 : 1); $i > $minimum; --$i)
											{
												// Get previous logs.
												try
													{
														include (self :: $directory . self :: $room ['data'] ['base'] . '.' . ($level - $i) . self :: $extension);
													}
												catch (Exception $exception)
													{
														// Add "we are missing messages" statement to the log.
														$last = end ($message_log);
														if (
															$last ['message'] != self :: $language ['not_contiguous_log']
														)
															{
																$message_log [] = array (
																	'message' => self :: $language ['not_contiguous_log']
																);
															}
													}
											}
									}

								// Get the current level's messages from memory.
								$current_log = self :: room_memory_get ('message_log');

								// Check if we even successfully got messages from memory.
								if (
									is_array ($current_log)
								)
									{
										// Merge it with the existing logs... This is if we loaded older logs above.
										$message_log = array_merge ($message_log, $current_log);
									}
								else
									{
										try
											{
												// Grab the current log from disk.
												include (self :: $directory . self :: $room ['data'] ['base'] . '.' . $level . self :: $extension);
											}
										catch (Exception $exception)
											{
												// Whatev'.
											}
									}
								
								++$levels;

								// Log for output.
								self :: $comments [] = 'retrieve: loaded ' . $levels . ' level' . (($levels > 1 or $levels == 0) ? 's' : '') . '.';

								// Get the total number of messages in the log.
								$total = count ($message_log);

								// This would be a little too confusing if I tried to combine it all into one ternary statement.
								if (
									(
										self :: $room ['temporary'] ['level'] != 0 or
										self :: $room ['temporary'] ['current'] != 0
									) and
									(
										self :: $room ['temporary'] ['level'] < ($level - 1) or
										(
											self :: $room ['temporary'] ['level'] < $level and
											self :: $room ['temporary'] ['current'] < self :: $room ['data'] ['messages']
										)
									)
								)
									{
										$start = self :: $room ['temporary'] ['current'];
									}
								elseif (
									self :: $room ['temporary'] ['level'] == 0 and
									self :: $room ['temporary'] ['current'] == 0 and
									$level > 0
								)
									{
										$start = (self :: $room ['data'] ['retrieve'] <= $total) ?
											($total - self :: $room ['data'] ['retrieve']) :
											0;
									}
								else
									{
										$start = $total - ($total - self :: $room ['temporary'] ['current']);
									}

								// Load users.
								$users_array = is_array ($users_array = self :: room_memory_get ('users')) ?
									$users_array :
									array ('guests' => array (), 'users' => array ('index' => array (), 'data' => array ()));

								// Create XML document.
								$document = new DOMDocument ('1.0', 'utf-8');

								// Create chat body.
								$moltx_chat = $document -> createElement ('moltx_chat');

								$input = array_merge ($input, array (
									'level' => $level,
									'line' => $line,
									'limit' => self :: $room ['data'] ['messages']
								));

								if (
											self :: $room ['temporary'] ['level'] == 0 and
											self :: $room ['temporary'] ['current'] == 0
								)
									{
										try
											{
												if (
													self :: $room ['data'] ['announcement'] != ''
												)
													{
														// Add messages to the front of the array.
														array_splice ($message_log, $start, 3, array (array ('message' => self :: $language ['room_connected']), array ('message' => self :: $room ['data'] ['header'])));

														$message_log [] = array (
															'message' => self :: $room ['data'] ['announcement']
														);
													}
												else
													{
														// Add messages to the front of the array.
														array_splice ($message_log, $start, 2, array (array ('message' => self :: $language ['room_connected']), array ('message' => self :: $room ['data'] ['header'])));
													}
											}
										catch (Exception $exception)
											{
												// Add messages to the front of the array.
												array_splice ($message_log, $start, 2, array (array ('message' => self :: $language ['room_connected']), array ('message' => self :: $room ['data'] ['header'])));
											}

										// Create actions root element.
										$actions = $document -> createElement ('actions');

										// Create "connected" action.
										$action = $document -> createElement ('action');
										$attribute = $document -> createAttribute ('type');
										$value = $document -> createTextNode ('connect');
										$attribute -> appendChild ($value);
										$action -> appendChild ($attribute);
										$actions -> appendChild ($action);
									}

								// Create messages root element.
								$messages = $document -> createElement ('messages');

								// Make XML.
								for ($i = $start; $i < $total; ++$i)
									{
										// Create a new message.
										$message = $document -> createElement ('message');

										// Check if there is a line associated with this message.
										if (
											isset ($message_log [$i] ['line'])
										)
											{
												$attribute = $document -> createAttribute ('line');
												$value = $document -> createTextNode ((string) $message_log [$i] ['line']);
												$attribute -> appendChild ($value);
												$message -> appendChild ($attribute);
											}

										// Check if there is a time associated with this message.
										if (
											isset ($message_log [$i] ['time'])
										)
											{
												$attribute = $document -> createAttribute ('time');
												$value = $document -> createTextNode ((string) $message_log [$i] ['time']);
												$attribute -> appendChild ($value);
												$message -> appendChild ($attribute);
											}

										// Check if there is a user associated with this message.
										if (
											isset ($message_log [$i] ['user'])
										)
											{
												// Get the location of the user in the data array.
												$index = array_search ($message_log [$i] ['user'], $users_array ['users'] ['index'], true);

												// Check if the user is in the data array.
												if (
													$index !== false
												)
													{
														// Create user element.
														$user = $document -> createElement ('by');

														// Check if there is a color associated with this user.
														if (
															$users_array ['users'] ['data'] [$index] ['color'] !== false
														)
															{
																$color = &$users_array ['users'] ['data'] [$index] ['color'];

																$attribute = $document -> createAttribute ('color');
																$value = $document -> createTextNode ($color ['red'] . ',' . $color ['green'] . ',' . $color ['blue']);
																$attribute -> appendChild ($value);
																$user -> appendChild ($attribute);
															}

														// Add user text to element.
														$text = $document -> createTextNode ((string) $message_log [$i] ['user']);
														$user -> appendChild ($text);

														// Add user element to message.
														$message -> appendChild ($user);
													}
											}

										// Add message body to message element.
										$content = $document -> createElement ('content');
										$text = $document -> createTextNode ((string) $message_log [$i] ['message']);
										$content -> appendChild ($text);
										$message -> appendChild ($content);

										// Add message to messages root element.
										$messages -> appendChild ($message);
									}
							}

						if (
							self :: check_users ()
						)
							{
								// Check if the DOMDocument object was already instantiated.
								if (
									!($document instanceof DOMDocument)
								)
									{
										// Load users.
										$users_array = is_array ($users_array = self :: room_memory_get ('users')) ?
											$users_array :
											array ('guests' => array (), 'users' => array ('index' => array (), 'data' => array ()));

										// Create XML document.
										$document = new DOMDocument ('1.0', 'utf-8');

										// Create chat body.
										$moltx_chat = $document -> createElement ('moltx_chat');
									}

								// Get the current action number.
								$action_number = is_integer ($action_number = self :: room_memory_get ('user')) ?
									$action_number :
									0;

								$input = array_merge ($input, array (
									'action' => $action_number,
									'guests' => count ($users_array ['guests']),
									'users' => count ($users_array ['users'] ['index'])
								));

								// Create users root element.
								$users = $document -> createElement ('users');

								// Make XML.
								for ($i = 0, $total = count ($users_array ['users'] ['data']); $i < $total; ++$i)
									{
										// Create a new user.
										$user = $document -> createElement ('user');

										// Check if there is a color associated with this user.
										if (
											$users_array ['users'] ['data'] [$i] ['color'] !== false
										)
											{
												$color = &$users_array ['users'] ['data'] [$i] ['color'];

												$attribute = $document -> createAttribute ('color');
												$value = $document -> createTextNode ($color ['red'] . ',' . $color ['green'] . ',' . $color ['blue']);
												$attribute -> appendChild ($value);
												$user -> appendChild ($attribute);
											}

										// Check if there is a time associated with this user.
										if (
											isset ($users_array ['users'] ['data'] [$i] ['time'])
										)
											{
												$attribute = $document -> createAttribute ('time');
												$value = $document -> createTextNode ((string) $users_array ['users'] ['data'] [$i] ['time']);
												$attribute -> appendChild ($value);
												$user -> appendChild ($attribute);
											}

										// Add user body to user element.
										$text = $document -> createTextNode ((string) $users_array ['users'] ['data'] [$i] ['name']);
										$user -> appendChild ($text);

										// Add user to users root element.
										$users -> appendChild ($user);
									}
							}

						if (
							$document instanceof DOMDocument
						)
							{
								// Add comments to document.
								self :: add_comments ($document);
								
								$input = array_merge ($input, array (
									'time' => time ()
								));

								ksort ($input);

								foreach ($input as $key => $set)
									{
										// Add data element.
										$data = $document -> createElement ('data');
										$attribute = $document -> createAttribute ('type');
										$value = $document -> createTextNode ((string) $key);
										$attribute -> appendChild ($value);
										$data -> appendChild ($attribute);
										$attribute = $document -> createAttribute ('value');
										$value = $document -> createTextNode ((string) $set);
										$attribute -> appendChild ($value);
										$data -> appendChild ($attribute);

										// Add data element to root element.
										$moltx_chat -> appendChild ($data);
									}

								if (
									$actions instanceof DOMElement
								)
									{
										// Add actions element to root element.
										$moltx_chat -> appendChild ($actions);
									}

								if (
									$messages instanceof DOMElement
								)
									{
										// Add messages root element to root element.
										$moltx_chat -> appendChild ($messages);
									}

								if (
									$users instanceof DOMElement
								)
									{
										// Add users root element to root element.
										$moltx_chat -> appendChild ($users);
									}

								// Append root element to document.
								$document -> appendChild ($moltx_chat);

								// Set document type to XML.
								header ('Content-Type: text/xml');

								// Fancy output equals yes!
								$document -> formatOutput = true;

								// Parse document into a string.
								$output = $document -> saveXML ();
							}

						if (
							$output !== false
						)
							{
								// Reset the error handler as to not interfere with code inheriting this object.
								restore_error_handler ();

								if (
									!$return
								)
									{
										// Print the output.
										echo $output;

										return true;
									}
								else
									{
										return $output;
									}
							}
					}
				else
					{
						// Create XML document.
						$document = new DOMDocument ('1.0', 'utf-8');

						// Add comments to document.
						self :: add_comments ($document);

						// Create chat body.
						$moltx_chat = $document -> createElement ('moltx_chat');

						// Create actions root element.
						$actions = $document -> createElement ('actions');

						$input = array (
							'connect' => (string) self :: $language ['room_not_exist'],
							'disconnect' => (string) self :: $language ['room_disconnected']
						);

						// Create "this room does not exist" message.
						// Create "disconnected" message."
						foreach ($input as $key => $set)
							{
								$action = $document -> createElement ('action');
								$attribute = $document -> createAttribute ('type');
								$value = $document -> createTextNode ($key);
								$attribute -> appendChild ($value);
								$action -> appendChild ($attribute);
								$text = $document -> createTextNode ($set);
								$action -> appendChild ($text);

								// Append action to actions root.
								$actions -> appendChild ($action);
							}

						// Append actions root element to root element.
						$moltx_chat -> appendChild ($actions);

						// Append root element to document.
						$document -> appendChild ($moltx_chat);

						// Set document type to XML.
						header ('Content-Type: text/xml');

						// Fancy output equals yes!
						$document -> formatOutput = true;

						// Parse document into a string.
						$output = $document -> saveXML ();

						// Reset the error handler as to not interfere with code inheriting this object.
						restore_error_handler ();

						if (
							!$return
						)
							{
								// Print the output.
								echo $output;

								return true;
							}
						else
							{
								return $output;
							}
					}

				// Create XML document.
				$document = new DOMDocument ('1.0', 'utf-8');
				
				self :: add_comments ($document);

				// Create chat body.
				$moltx_chat = $document -> createElement ('moltx_chat');

				// Append root element to document.
				$document -> appendChild ($moltx_chat);

				// Set document type to XML.
				header ('Content-Type: text/xml');

				// Fancy output equals yes!
				$document -> formatOutput = true;

				// Parse document into a string.
				$output = $document -> saveXML ();

				// Reset the error handler as to not interfere with code inheriting this object.
				restore_error_handler ();

				if (
					!$return
				)
					{
						// Print the output.
						echo $output;

						return true;
					}
				else
					{
						return $output;
					}
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: check_logs
		// Title: Check for log changes.
		// Variables: None.
		// Description: Checks if the message log changed since the last transaction.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function check_logs ()
			{
				// Get the current level and message number.
				//$level = self :: room_memory_get ('level');
				//$current = self :: room_memory_get ('current');
				$current = is_array ($current = self :: room_memory_get ('current')) ?
					$current :
					array ('level' => 0, 'line' => 0);
				$level = &$current ['level'];
				$line = &$current ['line'];

				if (
					$level > self :: $room ['temporary'] ['level'] or
					$line > self :: $room ['temporary'] ['current']
				)
					{
						return true;
					}

				return false;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: check_users
		// Title: Check for user changes.
		// Variables: None.
		// Description: Checks if the user list changed since the last transaction.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function check_users ()
			{
				// Get the current action number.
				$action = is_integer ($action = self :: room_memory_get ('user')) ?
					$action :
					0;

				if (
					$action > self :: $room ['temporary'] ['user']
				)
					{
						// Update the internal value.
						//self :: $room ['temporary'] ['user'] = $user;

						return true;
					}

				return false;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: check_function
		// Title: Check a function.
		// Variables: $user, $function
		//	1.	$user: The user the message is sent as.
		//	2.	$function: The function name.
		// Description: Check if a user is permitted to use a function.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function check_function ($user, $function)
			{
				return true;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: call_function
		// Title: Call a function.
		// Variables: $user, $function, $override
		//	1.	$user: The user the message is sent as.
		//	2.	$function: The function name.
		//	3.	$override: Whether or not to override the user's permissions.
		//		Default: false
		// Description: Calls a function if the user is permitted to do so.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function call_function ($user, $message, $override = false)
			{
				$arguments = array ();
				$function = false;
				$matches = array ();
				$method = '';

				$regex = array (
					'global' => '/(?<full>(^\\/)?[a-zA-Z_][0-9a-zA-Z_]*(\\s*\\=\\s*((\\s*,\\s*|(?<=\\=))(true|false|null|[0-9]+|[0-9]*\\.[0-9]+|(["\'])(?:\\\\?+.)*?\\7))+)?)/i',
					'arguments' => '/(?<key>^[a-zA-Z_][0-9a-zA-Z_]*)(\\s*\\=\\s*(?<value>((\\s*,\\s*|(?<=\\=))(true|false|null|[0-9]+|[0-9]*\\.[0-9]+|(["\'])(?:\\\\?+.)*?\\7))+))?/i',
					'values' => '/((\\s*,\\s*|(?<=^)\\s*)(?<values>true|false|null|[0-9]+|[0-9]*\\.[0-9]+|(["\'])(?:\\\\?+.)*?\\4))/i'
				);

				preg_match_all ($regex ['global'], $message, $matches);

				foreach ($matches ['full'] as $match)
					{
						if (
							!empty ($match)
						)
							{
								if (
									$match [0] != '/'
								)
									{
										// It's an argument.
										$variable = array ();
										$values = array ();

										// Split all the arguments into one array containing the key, and the other containing the value.
										preg_match ($regex ['arguments'], $match, $variable);

										if (
											isset ($variable ['value'])
										)
											{
												// Split the values into an array if there is an array of values.
												preg_match_all ($regex ['values'], $variable ['value'], $values);

												if (
													($count = count ($values ['values'])) === 1
												)
													{
														// Guess the proper encoding of the contents of this string.
														$value = self :: typecast ($values ['values'] [0]);

														// Strip any user-added escaping if it is a string.
														$arguments [$variable ['key']] = self :: strip_escaping ($value);
													}
												elseif (
													$count >= 0
												)
													{
														// Guess the proper encoding of the contents of this string.
														$value = self :: typecast ($values ['values']);

														// Strip any user-added escaping if it is a string. This works on arrays.
														$arguments [$variable ['key']] = self :: strip_escaping ($value);
													}
												// This should not happen.
												else
													{
														$arguments [$variable ['key']] = true;
													}
											}
										else
											{
												$arguments [$variable ['key']] = true;
											}
									}
								else
									{
										// It's the function.
										$function = substr ($match, 1);

										// Check if the function is permitted to be used.
										if (
											!$override and
											!self :: check_function ($user, $function)
										)
											{
												return false;
											}
									}
							}
					}

				// Get methods that exist.
				$methods = get_class_methods ('molten_chat_functions');

				// Check if the method can be called.
				if (
					$function != false and
					in_array ($function, $methods)
				)
					{
						try
							{
								// Variable functions rock!
								$result = molten_chat_functions :: $function ($user, $arguments);

								// True means don't send the function call, false means to send the function call.
								return $result;
							}
						catch (Exception $exception)
							{
								return false;
							}
					}
				
				return false;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: get_current
		// Title: Get current level and line from filesystem.
		// Variables: None.
		// Description: Guesses the current level and lines by using the filenames that exist for the room. Even if the level is higher than the level it guesses, the program will not break.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function get_current ()
			{
				self :: $comments [] = 'get_current: got level and line from disk.';

				// Create a regular expression to match all files that are log files.
				$regex = '/^' . preg_quote (self :: $room ['data'] ['base'], '/') . '\\.([0-9]+)\\.room\\.php$/';

				// Lock room.
				self :: lock_room (true);

				// Get a listing of all files in the rooms directory.
				$files = scandir (self :: $directory);

				// Get all files that are legit log files.
				$matches = preg_grep ($regex, $files);

				// Get levels from matches.
				$levels = preg_replace ($regex, '\\1', $matches);

				// Typecast levels to integers.
				foreach ($levels as &$level)
					{
						$level = (integer) $level;
					}
				unset ($level);

				// Sort the levels.
				sort ($levels);

				// Get highest level.
				$level = end ($levels);

				try
					{
						include (self :: $directory . self :: $room ['data'] ['base'] . '.' . $level . self :: $extension);

						// Count lines.
						$line = (($line = count ($message_log)) < self :: $room ['data'] ['messages']) ?
							$line :
							0;

						// Set message log.
						self :: room_memory_set ('message_log', $message_log);

						if (
							$line == 0
						)
							{
								// Increment level.
								++$level;
							}
					}
				catch (Exception $exception)
					{
						// It was deleted that fast? Wow!
						$line = 0;
					}

				$current = array (
					'level' => $level,
					'line' => $line
				);

				self :: room_memory_set ('current', $current);

				// Release room lock.
				self :: lock_room (false);

				return $current;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: get_server_identifier
		// Title: Get the server identifier.
		// Variables: None.
		// Description: Retrieves the current room identifier, or creates one if it doesn't exist. This function is Alternate PHP Cache Specific.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function get_room_identifier ()
			{
				// Get the current path of the server which is used to get or create a server identifier.
				$room_file = realpath (self :: $room ['files'] ['data']);

				// Get a listing of the current room identifiers.
				$room_identifiers = is_array ($room_identifiers = self :: global_memory_get ('room_identifiers')) ?
					$room_identifiers :
					array ();

				// Search for our room identifier.
				$index = array_search ($room_file, $room_identifiers, true);

				if (
					$index === false
				)
					{
						// Lock memory.
						self :: lock_memory (true);

						// Get a listing of the current room identifiers.
						$room_identifiers = is_array ($room_identifiers = self :: global_memory_get ('room_identifiers')) ?
							$room_identifiers :
							array ();

						// Create a new index.
						$index = count ($room_identifiers);

						// Add the file to the server index.
						$room_identifiers [$index] = $room_file;

						// Store the value.
						self :: global_memory_set ('room_identifiers', $room_identifiers, false);

						// Release memory lock.
						self :: lock_memory (false);
					}

				self :: $comments [] = 'get_room_identifier: got room identifier ' . $index . '.';

				// Return the index.
				return $index;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: get_server_identifier
		// Title: Get the server identifier.
		// Variables: None.
		// Description: Retrieves the current server identifier, or creates one if it doesn't exist. This function is Alternate PHP Cache Specific.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function get_server_identifier ()
			{
				// Get the current path of the server which is used to get or create a server identifier.
				$server_file = realpath (__FILE__);

				// Get a listing of the current server identifiers.
				$server_identifiers = is_array ($server_identifiers = self :: apc_get ('servers')) ?
					$server_identifiers :
					array ();

				// Search for our server identifier.
				$index = array_search ($server_file, $server_identifiers, true);

				if (
					$index === false
				)
					{
						// Lock global server space.
						self :: lock_global (true);

						// Get a listing of the current server identifiers again. It might have changed since the last time we did it.
						$server_identifiers = is_array ($server_identifiers = self :: apc_get ('servers')) ?
							$server_identifiers :
							array ();

						// Create a new index.
						$index = count ($server_identifiers);

						// Add the file to the server index.
						$server_identifiers [$index] = $server_file;

						// Store the value.
						self :: apc_set ('servers', $server_identifiers);

						// Release global server space lock.
						self :: lock_global (false);
					}

				self :: $comments [] = 'get_server_identifier: got server identifier ' . $index . '.';

				// Return the index.
				return $index;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: get_global_identifiers
		// Title: Get global memory identifiers.
		// Variables: None.
		// Description: Retrieves global memory identifiers, returns an empty array on failure. This function is Shared Memory Specific.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function get_global_identifiers ()
			{
				// Get the file identifier.
				$file_identifier = (isset (self :: $identifiers ['global'] ['identifiers'])) ?
					self :: $identifiers ['global'] ['identifiers'] :
					(self :: $identifiers ['global'] ['identifiers'] = ftok (__FILE__, '0'));

				// Get the identifiers from memory.
				$identifiers = self :: shmop_get ($file_identifier);

				// Check if we got a result.
				$identifiers = !is_null ($identifiers) ?
					$identifiers :
					array ();

				return $identifiers;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: set_global_identifier
		// Title: Set a global memory identifier.
		// Variables: $identifier, $lock
		//	1.	$identifier: The identifier to set.
		//	2.	$lock: If the system should lock the memory.
		//		Default: true
		// Description: Stores global memory identifier, returns the project identifier. This function is Shared Memory Specific.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function set_global_identifier ($identifier, $lock = true)
			{
				// Get the file identifier.
				$file_identifier = (isset (self :: $identifiers ['global'] ['identifiers'])) ?
					self :: $identifiers ['global'] ['identifiers'] :
					(self :: $identifiers ['global'] ['identifiers'] = ftok (__FILE__, '0'));

				if (
					$lock
				)
					{
						// Lock memory.
						self :: lock_memory (true);
					}

				// Get the old identifiers.
				$memory_old_data = self :: get_global_identifiers ();

				// Get the current value.
				$new_value = self :: $characters [count ($memory_old_data)];

				// Append the new data onto the old data.
				$memory_new_data = array_merge ($memory_old_data, array ($identifier => $new_value));

				// Add the new data into memory.
				self :: shmop_set ($file_identifier, $memory_new_data);

				if (
					$lock
				)
					{
						// Release memory lock.
						self :: lock_memory (false);
					}

				return $new_value;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: get_room_identifiers
		// Title: Get room memory identifiers.
		// Variables: None.
		// Description: Retrieves room memory identifiers, returns an empty array on failure. This function is Shared Memory Specific.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function get_room_identifiers ()
			{
				// Get the file identifier.
				$file_identifier = (isset (self :: $identifiers ['room'] ['identifiers'])) ?
					self :: $identifiers ['room'] ['identifiers'] :
					(self :: $identifiers ['room'] ['identifiers'] = ftok (self :: $room ['files'] ['data'], '0'));

				// Get the identifiers from memory.
				$identifiers = self :: shmop_get ($file_identifier);

				// Check if we got a result.
				$identifiers = !is_null ($identifiers) ?
					$identifiers :
					array ();

				return $identifiers;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: set_room_identifier
		// Title: Set a room memory identifier.
		// Variables: $identifier, $lock
		//	1.	$identifier: The identifier to set.
		//	2.	$lock:
		//		Default: true
		// Description: Stores room memory identifier, returns the project identifier. This function is Shared Memory Specific.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function set_room_identifier ($identifier, $lock = true)
			{
				// Get the file identifier.
				$file_identifier = (isset (self :: $identifiers ['room'] ['identifiers'])) ?
					self :: $identifiers ['room'] ['identifiers'] :
					(self :: $identifiers ['room'] ['identifiers'] = ftok (self :: $room ['files'] ['data'], '0'));

				if (
					$lock
				)
					{
						// Lock memory.
						self :: lock_memory (true);
					}

				// Get the old identifiers.
				$memory_old_data = self :: get_room_identifiers ();

				if (
					is_null ($memory_old_data)
				)
					{
						$memory_old_data = array ();
					}

				// Get the current value.
				$new_value = self :: $characters [count ($memory_old_data)];

				// Append the new data onto the old data.
				$memory_new_data = $memory_old_data;
				$memory_new_data [$identifier] = $new_value;

				// Add the new data into memory.
				self :: shmop_set ($file_identifier, $memory_new_data, false);

				if (
					$lock
				)
					{
						// Release memory lock.
						self :: lock_memory (false);
					}

				return $new_value;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: global_memory_get
		// Title: Get global memory data.
		// Variables: $identifier
		//	1.	$identifier: The data to get.
		// Description: Retrieves data from a memory block, returns null on failure.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function global_memory_get ($identifier)
			{
				switch (self :: $configuration ['cache'])
					{
						case 'apc':
							// Get the current server identifier.
							$server = isset (self :: $identifiers ['server']) ?
								self :: $identifiers ['server'] :
								(self :: $identifiers ['server'] = self :: get_server_identifier ());

							// Create the server key.
							$key = 'server_' . $server . '_' . $identifier;

							// Pie.
							return self :: apc_get ($key);
							break;

						case 'shmop':
							// Check if we have cached the global long variable.
							if (
								isset (self :: $identifiers ['global'] ['packages'] [$identifier])
							)
								{
									// Grab the cached long variable.
									$file_identifier = self :: $identifiers ['global'] ['packages'] [$identifier];
								}
							else
								{
									// Grab a listing of identifiers already in play.
									$identifiers = self :: get_global_identifiers ();

									// Check if the identifier we're using is in play.
									if (
										isset ($identifiers [$identifier])
									)
										{
											// Create a long variable from the identifier package in play.
											$file_identifier = self :: $identifiers ['global'] ['packages'] [$identifier] = ftok (__FILE__, $identifiers [$identifier]);
										}
								}

							// Check if the long variable was ever created, otherwise this data "doesn't exist."
							if (
								isset ($file_identifier)
							)
								{
									// Return the data.
									return self :: shmop_get ($file_identifier);
								}
							break;
					}

				return null;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: global_memory_set
		// Title: Set global memory data.
		// Variables: $identifier, $value, $lock
		//	1.	$identifier: The identifier to assign a value to.
		//	2.	$value: The value to set.
		//	3.	$lock: If the system should lock the memory.
		//		Default: true
		// Description: Sets data to a memory block, returns false on failure.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function global_memory_set ($identifier, $value, $lock = true)
			{
				switch (self :: $configuration ['cache'])
					{
						case 'apc':
							// Get the current server identifier.
							$server = isset (self :: $identifiers ['server']) ?
								self :: $identifiers ['server'] :
								(self :: $identifiers ['server'] = self :: get_server_identifier ());

							// Create the server key.
							$key = 'server_' . $server . '_' . $identifier;

							// Pie.
							return self :: apc_set ($key, $value, $lock);
							break;

						case 'shmop':
							// Check if we have cached the global long variable.
							if (
								isset (self :: $identifiers ['global'] ['packages'] [$identifier])
							)
								{
									// Grab the cached long variable.
									$file_identifier = self :: $identifiers ['global'] ['packages'] [$identifier];
								}
							else
								{
									// Grab a listing of identifiers already in play.
									$identifiers = self :: get_global_identifiers ();

									// Check if the identifier we're using is in play.
									if (
										isset ($identifiers [$identifier])
									)
										{
											// Create a long variable from the identifier package in play.
											$file_identifier = self :: $identifiers ['global'] ['packages'] [$identifier] = ftok (__FILE__, $identifiers [$identifier]);
										}
									else
										{
											// Generate a new long package identifier to put in play, and get it back.
											$package = self :: set_global_identifier ($identifier);

											// Create a long variable from the identifier package in play.
											$file_identifier = self :: $identifiers ['global'] ['packages'] [$identifier] = ftok (__FILE__, $package);
										}
								}

							// Check if the long variable was ever created, otherwise this data "doesn't exist."
							// I think this is redundant, we have an if {} else {if {} else {}} scenario. This means, no matter what, it should be set.
							if (
								isset ($file_identifier)
							)
								{
									// Returning the result doesn't say much but true on success, false (unlikely) on failure.
									return self :: shmop_set ($file_identifier, $value, $lock);
								}
							break;
					}

				return false;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: room_memory_get
		// Title: Get memory data.
		// Variables: $identifier
		//	1.	$identifier: The data to get.
		// Description: Retrieves data from a memory block for the current room, returns null on failure.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function room_memory_get ($identifier)
			{
				switch (self :: $configuration ['cache'])
					{
						case 'apc':
							// Get the current room identifier.
							$room = isset (self :: $identifiers ['room']) ?
								self :: $identifiers ['room'] :
								(self :: $identifiers ['room'] = self :: get_room_identifier ());

							// Get the current server identifier.
							$server = isset (self :: $identifiers ['server']) ?
								self :: $identifiers ['server'] :
								(self :: $identifiers ['server'] = self :: get_server_identifier ());

							// Create the server key.
							$key = 'room_' . $server . '_' . $room . '_' . $identifier;

							// Pie.
							return self :: apc_get ($key);
							break;

						case 'shmop':
							// Check if we have cached the room's long variable.
							if (
								isset (self :: $identifiers ['room'] ['packages'] [$identifier])
							)
								{
									// Grab the cached long variable.
									$file_identifier = self :: $identifiers ['room'] ['packages'] [$identifier];
								}
							else
								{
									// Grab a listing of identifiers already in play.
									$identifiers = self :: get_room_identifiers ();

									// Check if the identifier we're using is in play.
									if (
										isset ($identifiers [$identifier])
									)
										{
											// Create a long variable from the identifier package in play.
											$file_identifier = self :: $identifiers ['room'] ['packages'] [$identifier] = ftok (self :: $room ['files'] ['data'], $identifiers [$identifier]);
										}
								}

							// Check if the long variable was ever created, otherwise this data "doesn't exist."
							if (
								isset ($file_identifier)
							)
								{
									// Return the data.
									return self :: shmop_get ($file_identifier);
								}
							break;
					}

				return null;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: room_memory_set
		// Title: Set memory data.
		// Variables: $identifier, $value, $lock
		//	1.	$identifier: The identifier to assign a value to.
		//	2.	$value: The value to set.
		//	3.	$lock: If the system should lock the memory.
		//		Default: true
		// Description: Sets data to a memory block for the current room, returns false on failure.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function room_memory_set ($identifier, $value, $lock = true)
			{
				switch (self :: $configuration ['cache'])
					{
						case 'apc':
							// Get the current room identifier.
							$room = isset (self :: $identifiers ['room']) ?
								self :: $identifiers ['room'] :
								(self :: $identifiers ['room'] = self :: get_server_identifier ());

							// Get the current server identifier.
							$server = isset (self :: $identifiers ['server']) ?
								self :: $identifiers ['server'] :
								(self :: $identifiers ['server'] = self :: get_server_identifier ());

							// Create the server key.
							$key = 'room_' . $server . '_' . $room . '_' . $identifier;

							// Pie.
							return self :: apc_set ($key, $value, $lock);
							break;

						case 'shmop':
							// Check if we have cached the room's long variable.
							if (
								isset (self :: $identifiers ['room'] ['packages'] [$identifier])
							)
								{
									// Grab the cached long variable.
									$file_identifier = self :: $identifiers ['room'] ['packages'] [$identifier];
								}
							else
								{
									// Grab a listing of identifiers already in play.
									$identifiers = self :: get_room_identifiers ();

									// Check if the identifier we're using is in play.
									if (
										isset ($identifiers [$identifier])
									)
										{
											// Create a long variable from the identifier package in play.
											$file_identifier = self :: $identifiers ['room'] ['packages'] [$identifier] = ftok (self :: $room ['files'] ['data'], $identifiers [$identifier]);
										}
									else
										{
											// Generate a new long package identifier to put in play, and get it back.
											$package = self :: set_room_identifier ($identifier);

											// Create a long variable from the identifier package in play.
											$file_identifier = self :: $identifiers ['global'] ['packages'] [$identifier] = ftok (self :: $room ['files'] ['data'], $package);
										}
								}

							// Check if the long variable was ever created, otherwise this data "doesn't exist."
							// I think this is redundant, we have an if {} else {if {} else {}} scenario. This means, no matter what, it should be set.
							if (
								isset ($file_identifier)
							)
								{
									// Returning the result doesn't say much but true on success, false (unlikely) on failure.
									return self :: shmop_set ($file_identifier, $value, $lock);
								}
							break;
					}

				return null;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: apc_get
		// Title: Get memory data.
		// Variables: $key
		//	1.	$key: The key of the memory block.
		// Description: Retrieves data from cache, returns null on failure. This function is Alternate PHP Cache Specific.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function apc_get ($key)
			{
				// Append prefix to key.
				$key = 'molten_chat_' . $key;

				// Fetch the serialized data from memory.
				$block = apc_fetch ($key);

				if (
					is_string ($block)
				)
					{
						try
							{
								$unserialized = unserialize ($block);
							}
						catch (Exception $exception)
							{
								return null;
							}

						return $unserialized;
					}

				return null;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: apc_set
		// Title: Set memory data.
		// Variables: $key, $data, $lock
		//	1.	$key: The key of the memory block.
		//	2.	$data: The value to set it to.
		//		Default: false
		//	3.	$lock: If the system should lock the memory.
		//		Default: true
		// Description: Sets data to cache, returns false on failure. This function is Alternate PHP Cache Specific.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function apc_set ($key, $data = null, $lock = true)
			{
				// Append prefix to key.
				$key = 'molten_chat_' . $key;

				// Serialize current data.
				$new_block = serialize ($data);

				// Get the new block's size.
				$new_size = strlen ($new_block);

				if (
					$lock
				)
					{
						// Lock memory.
						self :: lock_memory (true);
					}
				
				// Check if the data is not empty.
				if (
					!is_null ($data) and
					$new_size > 0
				)
					{
						$old_block = is_string ($old_block = apc_fetch ($key)) ?
							$old_block :
							'';

						if (
							$new_block != $old_block
						)
							{
								apc_store ($key, $new_block, 0);
							}
					}
				else
					{
						// Remove value from memory.
						apc_delete ($key);
					}

				if (
					$lock
				)
					{
						// Release memory lock.
						self :: lock_memory (false);
					}

				return true;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: shmop_get
		// Title: Get memory data.
		// Variables: $long
		//	1.	$long: The long integer of the memory block.
		// Description: Retrieves data from a memory block, returns null on failure. This function is Shared Memory Specific.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function shmop_get ($long)
			{
				try
					{
						$memory_identifier = shmop_open ($long, 'a', 0, 0);
					}
				catch (Exception $exception)
					{
						$memory_identifier = false;
					}

				// Check if the data exists in memory.
				if (
					$memory_identifier
				)
					{
						// Get the block from memory.
						$memory_size = shmop_size ($memory_identifier);
						$memory_block = shmop_read ($memory_identifier, 0, $memory_size);
						shmop_close ($memory_identifier);

						try
							{
								$unserialized = unserialize ($memory_block);
							}
						catch (Exception $exception)
							{
								return null;
							}

						return $unserialized;
					}

				return null;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: shmop_set
		// Title: Set memory data.
		// Variables: $long, $data, $lock
		//	1.	$long: The long integer of the memory block.
		//	2.	$data: The value to set it to.
		//		Default: false
		//	3.	$lock: If the system should lock the memory.
		//		Default: true
		// Description: Sets data to a memory block, returns false on failure. This function is Shared Memory Specific.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function shmop_set ($long, $data = null, $lock = true)
			{
				$new = false;

				// Serialize current data.
				$memory_new_block = serialize ($data);

				// Get the new block's size.
				$memory_new_size = strlen ($memory_new_block);

				if (
					$lock
				)
					{
						// Lock memory.
						self :: lock_memory (true);
					}

				// Check if the data is not empty.
				if (
					!is_null ($data) and
					$memory_new_size > 0
				)
					{
						try
							{
								$memory_old_identifier = shmop_open ($long, 'w', 0, 0);
							}
						catch (Exception $exception)
							{
								$memory_old_identifier = false;
							}

						if (
							$memory_old_identifier
						)
							{
								// Get the current block's size.
								$memory_old_size = shmop_size ($memory_old_identifier);
								$memory_old_block = shmop_read ($memory_old_identifier, 0, $memory_old_size);
							}
						else
							{
								$new = true;
								$memory_old_size = 0;
								$memory_old_block = '';
							}

						// Check if the blocks are different, no point in going any further if there's no difference.
						if (
							$memory_new_block != $memory_old_block
						)
							{
								// Check if the current block exists.
								if (
									!empty ($memory_old_identifier)
								)
									{
										// Check if the new size differs from the current size.
										if (
											$memory_new_size != $memory_old_size
										)
											{
												$new = true;

												// Empty the value.
												shmop_write ($memory_old_identifier, str_repeat (' ', $memory_old_size), 0);

												// If it does, we delete it, so we can recreate it with the new size later on.
												shmop_delete ($memory_old_identifier);
											}

										shmop_close ($memory_old_identifier);
									}

								if (
									$new
								)
									{
										// Create new block to store new information in.
										$memory_new_identifier = shmop_open ($long, 'n', 0644, $memory_new_size);
									}
								else
									{
										// Open existing block to store new information in.
										$memory_new_identifier = shmop_open ($long, 'w', 0, 0);
									}

								// Write the new data.
								shmop_write ($memory_new_identifier, $memory_new_block, 0);
								shmop_close ($memory_new_identifier);
							}
					}
				else
					{
						try
							{
								$memory_old_identifier = shmop_open ($long, 'w', 0, 0);
							}
						catch (Exception $exception)
							{
								$memory_old_identifier = false;
							}

						if (
							$memory_old_identifier
						)
							{
								// Empty, then que the memory block for deletion.
								$memory_old_size = shmop_size ($memory_old_identifier);
								shmop_write ($memory_old_identifier, str_repeat (' ', $memory_old_size), 0);
								shmop_delete ($memory_old_identifier);
								shmop_close ($memory_old_identifier);
							}
					}

				if (
					$lock
				)
					{
						// Release memory lock.
						self :: lock_memory (false);
					}

				return true;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: add_comments
		// Title: Add comments.
		// Variables: $document
		//	1.	$document: The XML document to add comments to.
		// Description: Adds comments to a DOMDocument object.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function add_comments (&$document)
			{
				foreach (self :: $comments as $comment)
					{
						$comment = $document -> createComment ($comment);
						$document -> appendChild ($comment);
					}

				return true;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: strip_escaping
		// Title: Strip user-added escaping.
		// Variables: $data
		//	1.	$data: The data to be parsed.
		// Description: Strips user-added escaping to strings.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function strip_escaping ($data)
			{
				if (
					is_string ($data)
				)
					{
						// Get the type of quote used for this string.
						$type = $data [0];

						// Strip the quotes off the beginning and end of the string.
						$data = substr ($data, 1, -1);

						switch ($type)
							{
								case '\'':
									// Strip escaped slashes, and escaped quotes from this string.
									$data = str_replace (array ('\\\\', '\\\''), array ('\\', '\''), $data);
									break;
								case '"':
									// Strip escaped slashes, and escaped quotes from this string.
									$data = str_replace (array ('\\\\', '\\"'), array ('\\', '"'), $data);
									break;
							}
					}
				// This function works with multi-dimensional arrays.
				elseif (
					is_array ($data)
				)
					{
						foreach ($data as &$value)
							{
								// Recursively call self.
								$value = self :: strip_escaping ($value);
							}
						unset ($value);
					}

				return $data;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: typecast
		// Title: Cast variables to their proper type.
		// Variables: $data
		//	1.	$data: The data to be casted.
		// Description: Guesses the proper type of a string input by using various regular expressions.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function typecast ($data)
			{
				$regex = array (
					'boolean' => '/^(true|false)$/i',
					'null' => '/^null$/i',
					'integer' => '/^[0-9]+$/',
					'float' => '/^[0-9]*\\.[0-9]+$/',
					'string' => '(["\'])(?:\\\\?+.)*?\\1'
				);

				if (
					is_string ($data)
				)
					{
						switch (1)
							{
								// Check for a boolean.
								case preg_match ($regex ['boolean'], $data):
									$data = (!strcasecmp ($data, 'true')) ?
										true :
										false;
									break;

								// Check for a null.
								case preg_match ($regex ['null'], $data):
									$data = null;
									break;

								// Check for a integer.
								case preg_match ($regex ['integer'], $data):
									$data = (integer) $data;
									break;

								// Check for a float.
								case preg_match ($regex ['float'], $data):
									$data = (float) $data;
									break;
							}
					}
				// This function works with multi-dimensional arrays.
				elseif (
					is_array ($data)
				)
					{
						foreach ($data as &$value)
							{
								// Recursively call self.
								$value = self :: typecast ($value);
							}
						unset ($value);
					}

				return $data;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: typecast
		// Title: Cast variables to their proper type.
		// Variables: $data
		//	1.	$data: The data to be casted.
		// Description: Guesses the proper type of a string input by using various regular expressions. Will flatten multi-dimensional arrays!
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		public static function to_string ($data)
			{
				if (
					!is_string ($data)
				)
					{
						switch (gettype ($data))
							{
								// Not much needs to take place here, just typecast it.
								case 'double':
								case 'float': // In case double changes.
								case 'integer':
									$data = (string) $data;
									break;

								// Convert booleans to "true" and "false" textual display.
								case 'boolean':
									$data = $data ?
										'true' :
										'false';
									break;

								// Convert an array into a comma-separated list, flattens multi-dimensional arrays!
								case 'array':
									// Create a new single-dimensional array.
									$value = array ();

									foreach ($data as $key => $value)
										{
											if (
												!is_string ($value)
											)
												{
													// Get the string value of the item.
													$value = self :: to_string ($value);

													// Check if it could not be converted; is only true for resources!
													if (
														!is_null ($value)
													)
														{
															if (
																is_integer ($key)
															)
																{
																	$values [] = $value;
																}
															else
																{
																	// Add element to new single-dimensional array.
																	$values [] = '\'' . self :: parse_data ($key) . '\'=' . $value;
																}
														}
												}
											else
												{
													if (
														is_integer ($key)
													)
														{
															$values [] = '\'' . self :: parse_data ($value) . '\'';
														}
													else
														{
															// Add element to new single-dimensional array.
															$values [] = '\'' . self :: parse_data ($key) . '\'=\'' . self :: parse_data ($value) . '\'';
														}
												}
										}

									// Implode array with commas as the separator.
									$data = implode (', ', $values);
									break;

								// Create a textual representation of the null value.
								case 'NULL':
									$data = 'null';
									break;

								// Convert an object to its textual representation.
								case 'object':
									// Get object methods.
									$methods = get_class_methods ($data);

									// Check if the __to_string method exists.
									if (
										in_array ('__to_string', $methods)
									)
										{
											// Invoke it via PHP Magic Functions.
											$data = (string) $data;
										}
									else
										{
											// Write the class name.
											$data = get_class ($data);
										}
									break;

								// Return null instead of a string.
								default:
									$data = null;
									break;
							}
					}

				return $data;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: parse_text
		// Title: Parse output data for display.
		// Variables: $data
		//	1.	$data: The data to be parsed for output.
		// Description: Security and functionality function, clears all slashes from any external input for storage inside of memory.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function clean_data ($data)
			{
				if (
					is_string ($data)
				)
					{
						// Check if magic_quotes_gpc is on.
						if (
							get_magic_quotes_gpc () and
							// Fix for PHP 5.2.7.
							version_compare (PHP_VERSION, '5.2.7', '!=')
						)
							{
								$data = stripslashes ($data);
							}
					}

				return $data;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: parse_data
		// Title: Parse input data for string.
		// Variables: $data, $is_input
		//	1.	$data: The data to be parsed for storage in PHP files.
		//	2.	$is_input: Whether or not this is unfiltered input.
		//		Default: false
		// Description: Security and functionality function, allows you to store any data inside of a single-quoted PHP string.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function parse_data ($data, $is_input = false)
			{
				if (
					is_string ($data)
				)
					{
						// Check if magic_quotes_gpc is on.
						if (
							$is_input and
							get_magic_quotes_gpc () and
							// Fix for PHP 5.2.7.
							version_compare (PHP_VERSION, '5.2.7', '!=')
						)
							{
								$data = stripslashes ($data);
							}

						// Our custom "addslashes" specific to single quoted strings.
						$data = str_replace (array ('\\', '\''), array ('\\\\', '\\\''), $data);
					}

				return $data;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: parse_file
		// Title: Create filenames.
		// Variables: $data
		//	1.	$data: The data to be converted into a safe filename.
		// Description: Converts a name into a safe filename.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function parse_file ($data)
			{
				// Encode into safe filename.
				$file = base64_encode ($data);
				$file = str_replace ('/', '-', $file);

				// Fix for case-insensitive filesystems.
				$matches = array ();
				preg_match_all ('/[A-Z]/', $file, $matches);
				$matches = '_' . implode ($matches [0]);
				$file .= $matches;
				$file = strtolower ($file);

				return $file;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: lock_global
		// Title: Multi-instance locking.
		// Variables: $switch
		//	1.	$switch: Whether or not to lock out other threads.
		// Description: Prevents code from executing amongst other threads. This function is Alternate PHP Cache Specific.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function lock_global ($switch)
			{
				$lock = &self :: $lock ['global'];

				if (
					$switch and
					!$lock ['switch']
				)
					{
						// Acquire lock, this is a different kind of lock.
						while (!apc_add ('molten_chat_servers_lock', true, 1))
							{
								// Generate a random number.
								$random = mt_rand (0, 250);

								// Sleep for 0.000 to 0.250 seconds. seconds.
								usleep (1000 * $random);
							}

						$lock ['switch'] = true;

					}
				elseif (
					!$switch and
					$lock ['switch']
				)
					{
						// Release lock.
						apc_delete ('molten_chat_servers_lock');

						$lock ['switch'] = false;
					}
				else
					{
						return false;
					}
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: lock_memory
		// Title: Multi-instance locking.
		// Variables: $switch
		//	1.	$switch: Whether or not to lock out other threads.
		// Description: Prevents code from executing amongst other threads.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function lock_memory ($switch)
			{
				$lock = &self :: $lock ['memory'];

				$pointer = !empty ($lock ['pointer']) ?
					$lock ['pointer'] :
					($lock ['pointer'] = fopen (self :: $directory . 'memory.lock', 'a+'));

				if (
					$switch and
					!$lock ['switch']
				)
					{
						// Acquire lock without blocking.
						while (!flock ($pointer, LOCK_EX | LOCK_NB))
							{
								// Generate a random number.
								$random = mt_rand (0, 250);

								// Sleep for 0.000 to 0.250 seconds. seconds.
								usleep (1000 * $random);
							}

						$lock ['switch'] = true;
					}
				elseif (
					!$switch and
					$lock ['switch']
				)
					{
						// Release lock.
						flock ($pointer, LOCK_UN);

						$lock ['switch'] = false;
					}
				else
					{
						return false;
					}

				return true;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: lock_room
		// Title: Multi-instance locking.
		// Variables: $switch
		//	1.	$switch: Whether or not to lock out other threads.
		// Description: Prevents code from executing amongst other threads.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function lock_room ($switch)
			{
				$lock = &self :: $lock ['room'];

				$pointer = !empty ($lock ['pointer']) ?
					$lock ['pointer'] :
					($lock ['pointer'] = fopen (self :: $room ['files'] ['room'], 'a+'));

				if (
					$switch and
					!$lock ['switch']
				)
					{
						// Acquire lock without blocking.
						while (!flock ($pointer, LOCK_EX | LOCK_NB))
							{
								// Generate a random number.
								$random = mt_rand (0, 250);

								// Sleep for 0.000 to 0.250 seconds. seconds.
								usleep (1000 * $random);
							}

						$lock ['switch'] = true;
					}
				elseif (
					!$switch and
					$lock ['switch']
				)
					{
						// Release lock.
						flock ($pointer, LOCK_UN);

						$lock ['switch'] = false;
					}
				else
					{
						return false;
					}

				return true;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: lock_users
		// Title: Multi-instance locking.
		// Variables: $switch
		//	1.	$switch: Whether or not to lock out other threads.
		// Description: Prevents code from executing amongst other threads.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function lock_users ($switch)
			{
				$lock = &self :: $lock ['users'];

				$pointer = !empty ($lock ['pointer']) ?
					$lock ['pointer'] :
					($lock ['pointer'] = fopen (self :: $room ['files'] ['users'], 'a+'));

				if (
					$switch and
					!$lock ['switch']
				)
					{
						// Acquire lock without blocking.
						while (!flock ($pointer, LOCK_EX | LOCK_NB))
							{
								// Generate a random number.
								$random = mt_rand (0, 250);

								// Sleep for 0.000 to 0.250 seconds. seconds.
								usleep (1000 * $random);
							}

						$lock ['switch'] = true;
					}
				elseif (
					!$switch and
					$lock ['switch']
				)
					{
						// Release lock.
						flock ($pointer, LOCK_UN);

						$lock ['switch'] = false;
					}
				else
					{
						return false;
					}

				return true;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: database_connect
		// Title: Connect to database.
		// Variables: None.
		// Description: Connects to the database, whatever it may be.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function database_connect ()
			{
				if (
					!self :: $connected
				)
					{
						// Include database file.
						include (molten_chat_include . 'molten_chat_database.php');

						if (
							!molten_chat_database :: connect ()
						)
							{
								die ($language ['database_error']);
							}

						self :: $connected = true;
					}

				return true;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: collect_garbage
		// Title: Purge old information.
		// Variables: None.
		// Description: Scans the room list for old rooms, and deletes them from secondary storage. Removes levels that are old as well.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function collect_garbage ()
			{
				return self :: global_memory_set ('garbage', time ());
			}
	}
?>